`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2017/10/23 15:21:30
// Design Name: 
// Module Name: controller
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

//sl指令通过，data ram中需要将Generate address interface with 32bits勾选去掉即可正常跑通

module control_unit (
    input wire clk,
    rst,
    //decode stage
    input wire [5:0] opD,
    functD,
    input wire [4:0] rtD,
    output wire branchD,
    input wire [31:0] srca2D,
    srcb2D,
    output wire [1:0] jumpD,

    //execute stage
    input wire flushE,
    output wire [2:0] memtoregE,
    output wire alusrcE,
    output wire regdstE,
    regwriteE,
    output wire [5:0] alucontrolE,
    output wire [1:0] hilo_enE,  // 控制hiloreg写入
    output wire [1:0] hiloreg_in_typeE,  // 控制hiloreg的输入端从alu \ div \ mul来
    input wire stallE,  // 控制EX级流水线寄存器停滞
    output wire alu_srca_pcE,
    write_reg_31E,

    //mem stage
    output wire [2:0] memtoregM,
    output wire [3:0] memwriteM,
    output wire regwriteM,
    //write back stage
    output wire [2:0] memtoregW,
    output wire regwriteW

);

  //decode stage
  // wire [1:0] aluopD;
  reg [3:0] memwriteD;
  reg [2:0] memtoregD;
  wire alusrcD, regdstD, regwriteD;
  wire alu_srca_pcD, write_reg_31D;
  reg [5:0] alucontrolD;
  reg [1:0] hilo_enD, hiloreg_in_typeD;
  wire branch_tmpD;

  //execute stage
  wire [3:0] memwriteE;

  reg [7:0] controls;
  assign {regwriteD, regdstD, alusrcD, branch_tmpD, jumpD, alu_srca_pcD, write_reg_31D} = controls;

  always @(*) begin
    case (opD)
      6'b000000: begin
        case (functD)
          // 不写入reg
          6'b011010, 6'b011011: controls <= 8'b01000000;  //div, divu
          6'b011000, 6'b011001: controls <= 8'b01000000;  //mult, multu
          6'b010001, 6'b010011: controls <= 8'b01000000;  //mthi, mtlo
          6'b001000: controls <= 8'b00001000;  //jr
          6'b001001: controls <= 8'b11001010;  //jalr
          default: controls <= 8'b11000000;
        endcase
        case (functD)
          6'b100000: alucontrolD <= 6'd2;  //add
          6'b100010: alucontrolD <= 6'd4;  //sub
          6'b100100: alucontrolD <= 6'd0;  //and
          6'b100101: alucontrolD <= 6'd1;  //or
          6'b101010: alucontrolD <= 6'd5;  //slt
          6'b100110: alucontrolD <= 6'd10;  //xor
          6'b100111: alucontrolD <= 6'd11;  //nor

          6'b000000: alucontrolD <= 6'd12;  //sll
          6'b000010: alucontrolD <= 6'd13;  //srl
          6'b000011: alucontrolD <= 6'd14;  //sra
          6'b000100: alucontrolD <= 6'd15;  //sllv
          6'b000110: alucontrolD <= 6'd16;  //srlv
          6'b000111: alucontrolD <= 6'd17;  //srav
          6'b011010: alucontrolD <= 6'd18;  //div
          6'b011011: alucontrolD <= 6'd19;  //divu
          6'b010001: alucontrolD <= 6'd20;  //mthi
          6'b010011: alucontrolD <= 6'd21;  //mtlo
          6'b010010: alucontrolD <= 6'd22;  //mflo
          6'b010000: alucontrolD <= 6'd23;  //mfhi

          6'b100001: alucontrolD <= 6'd24;  //addu
          6'b100011: alucontrolD <= 6'd25;  //subu
          6'b101011: alucontrolD <= 6'd26;  //sltu
          6'b011000: alucontrolD <= 6'd27;  //mult
          6'b011001: alucontrolD <= 6'd28;  //multu

          6'b001000: alucontrolD <= 6'd0;  //jr
          6'b001001: alucontrolD <= 6'd29;  //jalr
          default:   alucontrolD <= 6'b000;
        endcase
      end  //R-TYRE
      /**************************************************************************************************/
      // 6'b100011: controls <= 8'b10100000;  //LW
      // 6'b101011: controls <= 9'b001000000;  //SW
      6'b000010: controls <= 8'b00000100;  //J

      6'b001100: begin
        controls <= 8'b10100000;
        alucontrolD <= 6'd6;
      end  // ANDI
      6'b001110: begin
        controls <= 8'b10100000;
        alucontrolD <= 6'd7;
      end  // XORI
      6'b001111: begin
        controls <= 8'b10100000;
        alucontrolD <= 6'd8;
      end  //LUI
      6'b001101: begin
        controls <= 8'b10100000;
        alucontrolD <= 6'd9;
      end  //ORI
      6'b001000: begin
        controls <= 8'b10100000;
        alucontrolD <= 6'd2;
      end  // ADDI
      6'b001001: begin
        controls <= 8'b10100000;
        alucontrolD <= 6'd24;
      end  // ADDIU
      6'b001010: begin
        controls <= 8'b10100000;
        alucontrolD <= 6'd5;
      end  // SLTI
      6'b001011: begin
        controls <= 8'b10100000;
        alucontrolD <= 6'd26;
      end  // SLTIU

      6'b000100, 6'b000101, 6'b000110: begin
        controls <= 8'b00010000;
        alucontrolD <= 6'd0;
      end  // BEQ, BNE, BGTZ, BLEZ, BLTZ, BGEZAL

      6'b000001: begin
        case (rtD)
          5'b10000, 5'b10001, 5'b00001: begin
            controls <= 8'b10010011;
            alucontrolD <= 6'd29;
          end  //BLTZAL BGEZAL 
          5'b00001: begin
            controls <= 8'b00010010;
            alucontrolD <= 6'd29;
          end  //BGEZ
          default: begin
            controls <= 8'b00010000;
            alucontrolD <= 6'd0;
          end
        endcase
      end

      6'b000011: begin
        controls <= 8'b10000111;
        alucontrolD <= 6'd29;
      end  // JAL

      6'b101000, 6'b101001, 6'b101011: begin
        controls <= 8'b00100000;
        alucontrolD <= 6'd24;
      end  // SB SH SW

      6'b100000, 6'b100100, 6'b100001, 6'b100101, 6'b100011: begin
        controls <= 8'b10100000;
        alucontrolD <= 6'd24;
      end  //LB LBU LH LHU LW


      default: begin
        controls <= 8'b00000000;
        alucontrolD <= 6'd0;
      end  //illegal op
    endcase


    /***************************************************************************************************/
    //判断memwriteD
    case (opD)
      6'b101000: memwriteD <= 4'b0001;  //SB
      6'b101001: memwriteD <= 4'b0011;  //SH
      6'b101011: memwriteD <= 4'b1111;  //SW
      default:   memwriteD <= 4'b0000;
    endcase

    /***************************************************************************************************/
    //判断memtoregD
    case (opD)
      6'b100000: memtoregD <= 3'b001;  //LB
      6'b100100: memtoregD <= 3'b101;  //LBU
      6'b100001: memtoregD <= 3'b010;  //LH
      6'b100101: memtoregD <= 3'b110;  //LHU
      6'b100011: memtoregD <= 3'b011;  //LW
      default:   memtoregD <= 3'b000;
    endcase
    /***************************************************************************************************/
    // 控制hilo_enE / hiloreg_in_typeE
    case (opD)
      6'b000000: begin
        case (functD)
          6'b011010, 6'b011011: begin
            hilo_enD <= 2'b11;
            hiloreg_in_typeD <= 2'b01;
          end  //div, divu
          6'b011000, 6'b011001: begin
            hilo_enD <= 2'b11;
            hiloreg_in_typeD <= 2'b00;
          end
          //mult,multu 先使用alu直接输出结果
          6'b010001: begin
            hilo_enD <= 2'b10;
            hiloreg_in_typeD <= 2'b00;
          end  //mthi
          6'b010011: begin
            hilo_enD <= 2'b01;
            hiloreg_in_typeD <= 2'b00;
          end  //mtlo
          default: begin
            hilo_enD <= 2'b00;
            hiloreg_in_typeD <= 2'b00;
          end
        endcase
      end
      default: begin
        hilo_enD <= 2'b00;
        hiloreg_in_typeD <= 2'b00;
      end
    endcase
  end

  /*********************************************************************************************************/

  // 只判断branch
  reg branch_judge;
  always @(*) begin
    case (opD)
      6'b000100: branch_judge <= srca2D == srcb2D;  // BEQ
      6'b000101: branch_judge <= srca2D != srcb2D;  // BNE
      6'b000110: branch_judge <= srca2D <= 0;  // BLEZ

      6'b000001: begin
        case (rtD)
          5'b00000: begin
            branch_judge <= $signed(srca2D) < 0;
          end  // BLTZ
          5'b10000: begin
            branch_judge <= $signed(srca2D) < 0;
          end  // BLTZAL
          5'b00001: begin
            branch_judge <= $signed(srca2D) >= 0;
          end  // BGEZ
          5'b10001: begin
            branch_judge <= $signed(srca2D) >= 0;
          end  // BGEZAL
        endcase
      end
      default: branch_judge <= 0;
    endcase
  end

  assign branchD = branch_tmpD & branch_judge;

  //pipeline registers
  flopenrc #(18) regE (
      clk,
      rst,
      ~stallE,
      flushE,
      {memtoregD, memwriteD, alusrcD, regdstD, regwriteD, alucontrolD, alu_srca_pcD, write_reg_31D},
      {memtoregE, memwriteE, alusrcE, regdstE, regwriteE, alucontrolE, alu_srca_pcE, write_reg_31E}
  );
  flopenr #(4) regE2 (
      clk,
      rst,
      ~stallE,
      {hilo_enD, hiloreg_in_typeD},
      {hilo_enE, hiloreg_in_typeE}
  );
  flopr #(8) regM (
      clk,
      rst,
      {memtoregE, memwriteE, regwriteE},
      {memtoregM, memwriteM, regwriteM}
  );
  flopr #(4) regW (
      clk,
      rst,
      {memtoregM, regwriteM},
      {memtoregW, regwriteW}
  );
endmodule
